package com.changzhi.service.impl;

import cn.hutool.core.util.RandomUtil;
import cn.hutool.http.HttpRequest;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.changzhi.common.constans.Constants;
import com.changzhi.common.constans.RedisConstans;
import com.changzhi.common.exception.ServiceException;
import com.changzhi.common.mq.MqProducer;
import com.changzhi.common.util.BaseUtil;
import com.changzhi.common.util.DateUtil;
import com.changzhi.common.util.Result;
import com.changzhi.common.util.SignUtils;
import com.changzhi.ldyl.model.*;
import com.changzhi.ldyl.model.dto.CarnivalBeastDto;
import com.changzhi.ldyl.model.dto.CarnivalBoxDto;
import com.changzhi.ldyl.model.dto.CarnivalCardDto;
import com.changzhi.ldyl.model.vo.*;
import com.changzhi.mapper.*;
import com.changzhi.service.*;
import com.changzhi.util.CommonUtil;
import com.changzhi.util.RedisUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;

/**
 * @Description:
 * @author: 小落
 * @date: 2021年01月12日
 */
@Slf4j
@Service
public class CarnivalServiceImpl implements CarnivalService {

	@Resource
	private ActivityRecordMapper activityRecordMapper;
	@Resource
	private ActivityCardService activityCardService;
	@Resource
	private ActivityCardMapper activityCardMapper;
	@Autowired
	private ChatChannelService chatChannelService;
	@Resource
	private GiftConfigMapper giftConfigMapper;
	@Autowired
	private UserService userService;
	@Autowired
	private RedisUtil redisUtil;
	@Autowired
	private ActivityService activityService;
	@Autowired
	private ActivityRecordService activityRecordService;
	@Autowired
	private GiftService giftService;
	@Resource
	private ActivityPrizeMapper activityPrizeMapper;
	@Resource
	private UserWalletMapper userWalletMapper;
	@Resource
	private Executor executor;
	@Autowired
	private MqProducer mqProducer;
	@Resource
	private SysDictMapper sysDictMapper;
	@Resource
	private ActivityDataMapper activityDataMapper;
	@Value("${host}")
	private String host;
	// 用户抽奖最大回合数-1
	private static Integer USER_TOTAL_ROUND = 100;

	public final static Integer ACTIVITY_ID_1 = 6;
	public final static Integer ACTIVITY_ID_2 = 7;
	//最大最怕张数
	public final static Integer MAX_LOCATION = 12;
	public final static DateTimeFormatter df = DateTimeFormatter.ofPattern("MM.dd HH");
	//拼图活动时间
	public final static Map<String, LocalDateTime> PUZZLE_TIME_MAP = new HashMap<>();
	//年兽活动时间
	public final static Map<String, LocalDateTime> BEAST_TIME_MAP = new HashMap<>();

	private ReentrantLock lock = new ReentrantLock();

	//碎片
	public static final Map<Integer,Integer> CHIP_MAP = new HashMap<>();
	//碎片背景图
	public static final Map<Integer, Map<Integer, String>> CHIP_BG_MAP = new HashMap<>();
	//每场年兽数据
	public static Map<Integer,CarnivalBeastVo> BEAST_MAP = new LinkedHashMap<>();
	//当前作战场次的实时年兽剩余血量
	public static AtomicInteger HP = new AtomicInteger(0);
	//当前作战场次每个用户对年兽的伤害量
	public static Map<String, AtomicInteger> HARM_MAP = new HashMap<>();
	//当前作战场次免费用户对年兽的伤害次数
	public static Map<String, AtomicInteger> HARM_COUNT_MAP = new HashMap<>();
	//当前作战场次最高一击、最后一击信息
	public static String maxHarmInfo = null;
	public static String lastHarmInfo = null;
	public static String maxHarmUserId = null;
	//当前回合年兽作战ID
	public static Integer ID = null;


	@Value("${h5.domain}")
	private String h5Domain;

	/*@PostConstruct
	public void init(){
		//活动1 拼图初始化....
		redisUtil.del(RedisConstans.ACTIVITY + ACTIVITY_ID_1);
		Activity activity1 = activityService.getActivity(ACTIVITY_ID_1);
		PUZZLE_TIME_MAP.put("startTime", activity1.getStartTime());
		PUZZLE_TIME_MAP.put("endTime", activity1.getEndTime());
		//存碎片
		JSONArray jsonArray1 = JSON.parseArray(activity1.getProperty1());
		for (int i = 0; i < jsonArray1.size(); i++) {
			CHIP_MAP.put(jsonArray1.getJSONObject(i).getInteger("giftId"), jsonArray1.getJSONObject(i).getInteger("num"));
		}
		//碎片背景图
		LambdaQueryWrapper<ActivityData> wrapper = new LambdaQueryWrapper<>();
		wrapper.eq(ActivityData::getActivityId, ACTIVITY_ID_1).orderByAsc(ActivityData::getProperty0).orderByAsc(ActivityData::getProperty1);
		List<ActivityData> list = activityDataMapper.selectList(wrapper);
		Map<String, List<ActivityData>> groupMap = list.stream().collect(Collectors.groupingBy(ActivityData::getProperty0));
		for (String id : groupMap.keySet()) {
			List<ActivityData> list1 = groupMap.get(id);
			Map<Integer, String> subMap = new HashMap<>();
			for (ActivityData activityData : list1) {
				subMap.put(Integer.valueOf(activityData.getProperty1()), activityData.getContent());
			}
			CHIP_BG_MAP.put(Integer.valueOf(id), subMap);
		}
		//活动2 年兽作战初始化....
		redisUtil.del(RedisConstans.ACTIVITY + ACTIVITY_ID_2);
		Activity activity2 = activityService.getActivity(ACTIVITY_ID_2);
		BEAST_TIME_MAP.put("startTime", activity2.getStartTime());
		BEAST_TIME_MAP.put("endTime", activity2.getEndTime());
		//初始化每场年兽数据
		initBeastInfo();

		//重启时查看缓存是否存在当前回合数据
		Map<String, Object> map = redisUtil.get(RedisConstans.CARNIVAL_BEAST_DATA + ID);
		if(map != null){
			Integer id = (Integer) map.get("ID");
			Integer hp = (Integer) map.get("HP");
			Map<String, Integer> harm_map = (Map<String, Integer>) map.get("HARM_MAP");
			Map<String, Integer> harm_count_map = (Map<String, Integer>) map.get("HARM_COUNT_MAP");
			String maxHarm = (String) map.get("maxHarmInfo");
			String lastHarm= (String) map.get("lastHarmInfo");
			if(hp != null){
				HP = new AtomicInteger(hp);
			}
			if(harm_map != null){
				Set<Map.Entry<String, Integer>> entries = harm_map.entrySet();
				for (Map.Entry<String, Integer> entry : entries) {
					HARM_MAP.put(entry.getKey(), new AtomicInteger(entry.getValue()));
				}
			}
			if(harm_count_map != null){
				Set<Map.Entry<String, Integer>> entries = harm_count_map.entrySet();
				for (Map.Entry<String, Integer> entry : entries) {
					HARM_COUNT_MAP.put(entry.getKey(), new AtomicInteger(entry.getValue()));
				}
			}
			if(StringUtils.isNotBlank(maxHarm)){
				maxHarmInfo = maxHarm;
			}
			if(StringUtils.isNotBlank(lastHarm)){
				lastHarmInfo = lastHarm;
			}
		}
	}*/


	@Override
	public void doMqDataByUserGift(UserGift userGift) {
		//2.是否在活动时间内
		if(!DateUtil.isBetweenTime(PUZZLE_TIME_MAP.get("startTime"), PUZZLE_TIME_MAP.get("endTime"))){
			return;
		}
		//是否为碎片
		Integer num = CHIP_MAP.get(userGift.getGiftId());
		if(num == null){
			return;
		}
		//查用户
		User user = userService.getUser(userGift.getUserId());
		if (user == null) {
			return;
		}
		//查礼物配置
		GiftConfig giftConfig = giftConfigMapper.selectById(userGift.getGiftId());
		if (giftConfig == null) {
			return;
		}
		for (int i = 0; i < userGift.getGiftCount(); i++) {
			puzzle(userGift, user.getNickname(), num);
			try {
				Thread.sleep(500);
			} catch (InterruptedException e) {
				log.error("===========:{}", JSON.toJSONString(userGift));
				e.printStackTrace();
			}
		}
	}

	//拼图
	private void puzzle(UserGift userGift, String nickname,Integer num) {
		String channelId = userGift.getChannelId();
		//获取关卡
		Integer level = 1;
		LambdaQueryWrapper<ActivityCard> wrapper = new LambdaQueryWrapper<>();
		wrapper.eq(ActivityCard::getActivityId,ACTIVITY_ID_1).eq(ActivityCard::getChannelId, channelId).eq(ActivityCard::getLocation, num);
		wrapper.orderByDesc(ActivityCard::getLevel).last("limit 1");
		ActivityCard activityCard = activityCardMapper.selectOne(wrapper);
		if (activityCard != null) {
			level = activityCard.getLevel() + 1;
		}
		//查询当前关卡是否拼图完成
		boolean finish = false;
		LambdaQueryWrapper<ActivityCard> queryWrapper = new LambdaQueryWrapper<>();
		queryWrapper.eq(ActivityCard::getActivityId,ACTIVITY_ID_1).eq(ActivityCard::getChannelId, channelId).eq(ActivityCard::getLevel, level);
		queryWrapper.orderByAsc(ActivityCard::getLocation);
		List<ActivityCard> cardList = activityCardMapper.selectList(queryWrapper);
		if(cardList.size() >= MAX_LOCATION - 1){
			finish = true;
		}
		ActivityCard card = new ActivityCard();
		card.setActivityId(ACTIVITY_ID_1);
		card.setChannelId(channelId);
		card.setUserId(userGift.getUserId());
		card.setLevel(level);
		card.setLocation(num);
		card.setGiftId(userGift.getGiftId());
		card.setName(nickname);
		card.setIcon(userGift.getGiftIconUrl());
		card.setNum(1);
		card.setStatus(finish ? 2 : 1);
		activityCardMapper.insert(card);
		//判断本张拼图是否完成
		if(finish){
			//-1.加假人气
			addChannelRobotByFirst(channelId, userGift.getId());
			//0.有宝箱，api判断此值，有才请求宝箱接口
			redisUtil.set(RedisConstans.CARNIVAL_BOX, 1, RedisConstans.ONE_DAY);
			Long id = userGift.getId();
			//1.拼图完成，发放奖励
			finishPuzzlePrize(cardList, card);
			//2.生成开始时间
			LocalDateTime now = LocalDateTime.now();
			Long startTime = now.plusMinutes(1).toEpochSecond(ZoneOffset.of("+8"));
			//3.存宝箱到redis
			List<Map<String,Object>> cacheBoxList = redisUtil.hGetValuesList(RedisConstans.CARNIVAL_BOX_INFO + channelId);
			if(CollectionUtils.isEmpty(cacheBoxList)){
				Map<String,Object> map = new HashMap<>();
				map.put("id", id);
				map.put("startTime", startTime);
				redisUtil.hset(RedisConstans.CARNIVAL_BOX_INFO + channelId, id.toString(), map);
			} else {
				Map<String, Object> cacheMap = cacheBoxList.get(cacheBoxList.size() - 1);
				Map<String,Object> map = new HashMap<>();
				map.put("id", id);
				redisUtil.hset(RedisConstans.CARNIVAL_BOX_INFO + channelId, id.toString(), map);
			}
			//4.发送宝箱消息到mq
			/*CarnivalBoxDto carnivalBoxDto = new CarnivalBoxDto();
			carnivalBoxDto.setChannelId(channelId);
			mqProducer.sendBoxInfo(JSON.toJSONString(showBox(carnivalBoxDto)));*/

			//5.有推送，api判断此值，有才请求推送接口
			redisUtil.set(RedisConstans.CARNIVAL_PUSH, 1, RedisConstans.TWO_MINUTES);
			//6.发送厅内横幅到mq
			ChatChannel chatChannel = chatChannelService.getChatChannel(channelId);
			if (chatChannel == null) {
				return;
			}
			Map<String, Object> message = new HashMap<>();
			message.put("content", String.format("<span style=\"font-size: 13px;font-weight: bold;color: white\">恭喜<font color=\"#FFEF39\">%s厅</font>成功完成一张拼图！宝箱奖励将于1分钟后开启</span>", chatChannel.getChannelName()));
			message.put("linkValue", chatChannel.getChannelId());
			message.put("linkType", 1);
			message.put("id", id);
			message.put("startTime", startTime);
			if(getSwitch()){
				log.error("拼图走的是长连接....");
				mqProducer.sendPushInfo(JSON.toJSONString(message));
			} else {
				//存储横幅消息到redis（兼容）
				redisUtil.hset(RedisConstans.CARNIVAL_PUSH_BOX, id.toString(), message, RedisConstans.ONE_MINUTES);
			}
			//7.存拼图完成存到redis中
			redisUtil.hset(RedisConstans.CARNIVAL_PUZZLE_FINISH, channelId, channelId + level, RedisConstans.ONE_MONTH);
			redisUtil.del(RedisConstans.CARNIVAL_PUZZLE_RANKINGS);
		}
	}

	//添加假人气
	@Override
	public void addChannelRobotByFirst(String channelId, Long id) {
		executor.execute(()->{
			long start = System.currentTimeMillis();
			int roundIndex = id.intValue();
			//存缓存
			LocalDateTime now = LocalDateTime.now();
			LocalDateTime endTime = now.plusMinutes(11);
			Map<String, Object> map = new HashMap<>();
			map.put("endTime", DateUtil.localDateTimeToString(endTime));
			map.put("channelId", channelId);
			map.put("roundIndex", roundIndex);
			redisUtil.hset(RedisConstans.CHANNEL_ROBOT, channelId + "_" +id, map);
			redisUtil.hset(RedisConstans.CHANNEL_ROBOT1, channelId + "_" +id, map);
			//请求api
			Map<String, Object> params = new HashMap<>();
			params.put("channelId", channelId);
			params.put("type", 1);
			params.put("num", 500);//todo
			params.put("pwd", "8704");
			params.put("roundIndex", roundIndex);
			String requestBody = JSON.toJSONString(params);
			log.warn("methodName:addChannelRobotByFirst,requestBody={}",requestBody);
			HttpRequest request = HttpRequest.post(host + "/rest/activity/addChannelRobotByFirst").header("sign", SignUtils.sign(params)).body(requestBody);
			String response = request.execute().body();
			log.warn("methodName:addChannelRobotByFirst,response={}",response);
			Result result = JSON.parseObject(response, Result.class);
			if(result.getCode() != 0){
				log.error("methodName:addChannelRobotByFirst,request={},response={}", requestBody, response);
			}
			log.warn("methodName:addChannelRobotByFirst,耗时：{}", System.currentTimeMillis() - start);
		});
	}


	@Override
	public void clearChannelReBoot() {
		List<Map<String, Object>> channelIdList = redisUtil.hGetValuesList(RedisConstans.CHANNEL_ROBOT);
		if(CollectionUtils.isEmpty(channelIdList)){
			return;
		}
		for (Map<String, Object> map : channelIdList) {
			LocalDateTime now = LocalDateTime.now();
			String endTimeStr = (String) map.get("endTime");
			LocalDateTime endTime = DateUtil.stringToLocalDateTime(endTimeStr);
			if(now.isAfter(endTime)){
				String channelId = (String) map.get("channelId");
				Integer roundIndex = (Integer) map.get("roundIndex");
				executor.execute(()->{
					long start = System.currentTimeMillis();
					//请求api
					Map<String, Object> params = new HashMap<>();
					params.put("channelId", channelId);
					params.put("type", 0);
					params.put("pwd", "8704");
					params.put("roundIndex", roundIndex);
					String requestBody = JSON.toJSONString(params);
					log.warn("methodName:clearChannelReBoot,requestBody={}",requestBody);
					HttpRequest request = HttpRequest.post(host + "/rest/activity/addChannelRobotByFirst").header("sign", SignUtils.sign(params)).body(requestBody);
					String response = request.execute().body();
					//log.warn("methodName:clearChannelReBoot,response={}",response);
					Result result = JSON.parseObject(response, Result.class);
					if(result.getCode() != 0){
						log.error("methodName:clearChannelReBoot,耗时={},request={},response={}", System.currentTimeMillis() - start,requestBody,response);
					} else {
						log.warn("methodName:clearChannelReBoot完成,耗时={},request={},response={}", System.currentTimeMillis() - start,requestBody,response);
						redisUtil.hdel(RedisConstans.CHANNEL_ROBOT, channelId + "_" + roundIndex);
					}
				});
			}

		}
	}

	//拼图完成，发放奖励
	private void finishPuzzlePrize(List<ActivityCard> cardList, ActivityCard activityCard){
		executor.execute(()->{
			cardList.add(activityCard);
			cardList.sort(Comparator.comparing(ActivityCard::getLocation));
			List<ActivityRecord> recordList = new ArrayList<>();
			for (int i = 0; i < cardList.size(); i++) {
				ActivityCard card = cardList.get(i);
				Integer type;
				if(card.getLocation() <= 6){
					type = 0;
				} else{
					type = 1;
				}
				NewLotteryVo lotteryVo = lotterySimple(ACTIVITY_ID_1, type, null);
				ActivityRecord record = new ActivityRecord();
				record.setActivityId(ACTIVITY_ID_1);
				record.setType(type);
				record.setUserId(card.getUserId());
				record.setGiftId(lotteryVo.getGiftId());
				record.setValue(lotteryVo.getValue() * lotteryVo.getNum());
				record.setContent(lotteryVo.getName() + "*" + lotteryVo.getNum());
				record.setStatus(0);
				record.setNum(lotteryVo.getNum());
				record.setLevel(activityCard.getLevel());
				record.setGroupId(activityCard.getChannelId());
				recordList.add(record);
			}
			//批量插入数据库
			activityRecordService.saveBatch(recordList);
		});
	}

	@Override
	public CarnivalCardVo cardList(CarnivalCardDto carnivalCardDto) {
		String userId = carnivalCardDto.getUserId();
		Integer accountId = carnivalCardDto.getAccountId();
		String channelId = "";
		if(accountId == null){
			channelId = redisUtil.get(RedisConstans.CARNIVAL_LAST_CHANNEL_ID + userId);
			if(StringUtils.isBlank(channelId)){
				throw new ServiceException(-2, "厅ID不能为空");
			}
		} else {
			//查用户数据
			User user = userService.getUserByAccountId(accountId);
			if (user == null) {
				throw new ServiceException("该厅不存在");
			}
			channelId = user.getChannelId();
		}

		//查询房间数据
		ChatChannel chatChannel = chatChannelService.getChatChannel(channelId);
		if (chatChannel == null) {
			throw new ServiceException("房间数据不存在");
		}
		Integer level = carnivalCardDto.getLevel();
		//查询最大张数
		Integer maxLevel = activityCardMapper.selectMaxLevel(ACTIVITY_ID_1, channelId, null);
		boolean max = false;
		//默认显示 不完整的第一张
		if(level == null){
			level = activityCardMapper.selectMaxLevel(ACTIVITY_ID_1, channelId, 2);
			if(maxLevel.equals(level)){
				max = true;
			}
		} else {
			//是否越级
			if(maxLevel.equals(level) || (maxLevel==0)){
				max = true;
			} else if(maxLevel < level){
				throw new ServiceException("已经到底了");
			}
		}
		//查列表
		LambdaQueryWrapper<ActivityCard> wrapper = new LambdaQueryWrapper<>();
		wrapper.eq(ActivityCard::getActivityId, ACTIVITY_ID_1).eq(ActivityCard::getChannelId, channelId).eq(ActivityCard::getLevel, level);
		List<ActivityCard> list = activityCardService.list(wrapper);
		//查询完成拼图数
		int count = activityCardService.count(new LambdaQueryWrapper<ActivityCard>().eq(ActivityCard::getActivityId, ACTIVITY_ID_1).eq(ActivityCard::getChannelId, channelId).eq(ActivityCard::getStatus, 2));
		//返回
		CarnivalCardVo result = new CarnivalCardVo();
		int bg_index = level % 10;
		bg_index = bg_index == 0 ? 10 : bg_index;//将0修改为10
		List<CarnivalCardVo.Card> cardList = new ArrayList<>();
		Map<Integer, List<ActivityCard>> groupMap = list.stream().collect(Collectors.groupingBy(ActivityCard::getLocation));
		for (int i = 1; i <= MAX_LOCATION; i++) {
			CarnivalCardVo.Card record = result.new Card();
			List<ActivityCard> cards = groupMap.get(i);
			record.setLocation(i);
			record.setIcon(CHIP_BG_MAP.get(bg_index).get(i));
			if(!CollectionUtils.isEmpty(cards)){
				record.setNickname(cards.get(0).getName());
				record.setStatus(1);
			} else {
				record.setStatus(0);
			}
			cardList.add(record);
		}
		result.setNum(count)
				.setMax(max)
				.setLevel(level)
				.setAccountId(chatChannel.getOwnerAccountid())
				.setChannelId(chatChannel.getChannelId())
				.setChannelName(chatChannel.getChannelName())
				.setChannelIcon(StringUtils.isNotBlank(chatChannel.getAvatarUrl()) ? chatChannel.getAvatarUrl() : Constants.DEFAULT_AVATAR)
				.setCardList(cardList);
		redisUtil.set(RedisConstans.CARNIVAL_LAST_CHANNEL_ID + userId, channelId, RedisConstans.ONE_WEEK);
		return result;
	}


	@Override
	@Transactional(rollbackFor = Exception.class)
	public List<GiftConfigVo> receiveAward(CarnivalCardDto carnivalCardDto) {
		String channelId = carnivalCardDto.getChannelId();
		if(StringUtils.isBlank(channelId)){
			throw new ServiceException("厅id不能为空");
		}
		//2.是否在活动时间内
		if(!DateUtil.isBetweenTime(PUZZLE_TIME_MAP.get("startTime"), PUZZLE_TIME_MAP.get("endTime"))){
			throw new ServiceException("不在活动时间内");
		}
		List<GiftConfigVo> resultList = new ArrayList<>();
		LambdaQueryWrapper<ActivityRecord> wrapper = new LambdaQueryWrapper<>();
		wrapper.eq(ActivityRecord::getActivityId, ACTIVITY_ID_1).eq(ActivityRecord::getUserId, carnivalCardDto.getUserId()).eq(ActivityRecord::getGroupId, channelId);
		wrapper.orderByAsc(ActivityRecord::getId);
		if(carnivalCardDto.getLevel() != null){
			//领取当前关卡奖励
			wrapper.eq(ActivityRecord::getLevel, carnivalCardDto.getLevel());
		} else{
			wrapper.eq(ActivityRecord::getStatus, 0);
		}
		List<ActivityRecord> list = activityRecordService.list(wrapper);
		if(CollectionUtils.isEmpty(list)){
			throw new ServiceException("目前您的碎片还没有成功完成拼图喔~请再接再厉OvO");
		}
		if(carnivalCardDto.getLevel() != null){
			Map<Integer, List<ActivityRecord>> statusGroup = list.stream().collect(Collectors.groupingBy(ActivityRecord::getStatus));
			list = statusGroup.get(0);
			if(CollectionUtils.isEmpty(list)){
				throw new ServiceException("本张拼图奖励已领完");
			}
		}
		//更改状态
		for (ActivityRecord activityRecord : list) {
			int row = activityRecordMapper.updateByStatus(activityRecord.getId());
			if(row < 1){
				throw new ServiceException("领取失败,请稍后重试");
			}
			//放入背包
			GiftConfig giftConfig = giftService.savePrize(ACTIVITY_ID_1, carnivalCardDto.getUserId(), activityRecord.getGiftId(), null, activityRecord.getNum());
			GiftConfigVo result = new GiftConfigVo();
			result.setGiftId(giftConfig.getId()).setName(giftConfig.getName()).setIcon(giftConfig.getIconUrl()).setNum(activityRecord.getNum());
			resultList.add(result);
		}
		//合并奖品
		List<GiftConfigVo> prizeList = new ArrayList<>();
		Map<Integer, List<GiftConfigVo>> groupMap = resultList.stream().collect(Collectors.groupingBy(GiftConfigVo::getGiftId));
		Set<Map.Entry<Integer, List<GiftConfigVo>>> entries = groupMap.entrySet();
		for (Map.Entry<Integer, List<GiftConfigVo>> entry : entries) {
			List<GiftConfigVo> lotteryList = entry.getValue();
			GiftConfigVo lotteryVo = lotteryList.get(0);
			int num = 0;
			for (GiftConfigVo lotteryVo1 : lotteryList) {
				num += lotteryVo1.getNum();
			}
			lotteryVo.setNum(num);
			prizeList.add(lotteryVo);
		}
		return prizeList;
	}


	@Override
	public CarnivalBoxVo showBox(CarnivalBoxDto carnivalBoxDto) {
		if(carnivalBoxDto.getChannelId() == null){
			throw new ServiceException("频道id不能为空");
		}
		String key = RedisConstans.CARNIVAL_BOX_INFO + carnivalBoxDto.getChannelId();
		List<Map<String, Object>> mapList = redisUtil.hGetValuesList(key);
		if(CollectionUtils.isEmpty(mapList)){
			return null;
		}
		mapList.sort((Map<String, Object> s1, Map<String, Object> s2) -> (int) ((Long) s1.get("id") - (Long) s2.get("id")));


		Long currentTime = LocalDateTime.now().toEpochSecond(ZoneOffset.of("+8"));
		Iterator<Map<String, Object>> iterator = mapList.iterator();
		while (iterator.hasNext()){
			Map<String, Object> map = iterator.next();
			Long id = (Long) map.get("id");
			Long startTime = (Long) map.get("startTime");
			if(startTime == null){
				startTime = currentTime + 60;
				map.put("startTime", startTime);
				redisUtil.hset(key, id.toString(), map);
			}
			if(startTime + 60 < currentTime){
				iterator.remove();
				redisUtil.hdel(key, id.toString());
				continue;
			}
			Long surplus = startTime - currentTime;
			CarnivalBoxVo result = new CarnivalBoxVo();
			result.setBoxId((Long) map.get("id"))
					.setNum(mapList.size())
					.setSurplus(surplus < 0 ? 0L :surplus)
					.setStartTime(startTime)
					.setTimeStamp(currentTime);
			return result;
		}
		return null;
	}

	/**
	 * status,状态：
	 * 	0=领取未开始，请您耐心等候~
	 * 	1=恭喜你！抢到宝箱奖励！已放入【背包】
	 * 	2=很抱歉！宝箱被抢完啦！下次再来吧~
	 * 	3=宝箱已抢完！下一个宝箱稍后开抢！
	 * 	4=您已抢过此宝箱
	 * @param carnivalBoxDto
	 * @return
	 */
	@Override
	public Map<String, Object> openBox(CarnivalBoxDto carnivalBoxDto) {
		//2.是否在活动时间内
		if(!DateUtil.isBetweenTime(PUZZLE_TIME_MAP.get("startTime"), PUZZLE_TIME_MAP.get("endTime"))){
			throw new ServiceException("不在活动时间内");
		}
		String userId = carnivalBoxDto.getUserId();
		Integer boxId = carnivalBoxDto.getBoxId();
		String channelId = carnivalBoxDto.getChannelId();
		if(userId == null){
			throw new ServiceException("用户id不能为空");
		}
		if(boxId == null){
			throw new ServiceException("宝箱id不能为空");
		}
		if(StringUtils.isBlank(channelId)){
			throw new ServiceException("频道id不能为空");
		}
		Map<String, Object> result = new HashMap<>();
		String key = RedisConstans.CARNIVAL_BOX_INFO + channelId;
		List<Map<String, Object>> mapList = redisUtil.hGetValuesList(key);
		if(CollectionUtils.isEmpty(mapList)){
			result.put("status", 2);
			return result;
		}
		if(mapList.size() > 1){
			mapList.sort((Map<String, Object> s1, Map<String, Object> s2) -> (int) ((Long) s1.get("id") - (Long) s2.get("id")));
		}
		//判断时间
		Map<String, Object> map = mapList.get(0);
		Long startTime = (Long) map.get("startTime");
		if(startTime == null){
			result.put("status", 3);
			return result;
		}
		Long currentTime = LocalDateTime.now().toEpochSecond(ZoneOffset.of("+8"));
		if(currentTime < startTime){
			result.put("status", 0);
			return result;
		}
		//获取宝箱奖池
		List<ActivityPrize> prizeList = getBoxBool();
		int size = prizeList.size();
		//判断用户是否已经抢到宝箱
		if(redisUtil.hget(RedisConstans.CARNIVAL_BOX_USER + boxId, userId) != null){
			Integer count = redisUtil.get(RedisConstans.CARNIVAL_BOX_AWARDED_NUM + boxId);
			if(count > size && mapList.size() > 1){
				result.put("status", 3);
				return result;
			}
			result.put("status", 4);
			return result;
		}
		//增加一个抢到人数
		long incr = redisUtil.incr(RedisConstans.CARNIVAL_BOX_AWARDED_NUM + boxId);
		if(incr > size){
			if(mapList.size() == 1){
				result.put("status", 2);
			}else{
				result.put("status", 3);
			}
			return result;
		}
		//========成功抢到宝箱往下========
		redisUtil.hset(RedisConstans.CARNIVAL_BOX_USER + boxId, userId, 1,RedisConstans.ONE_WEEK);
		redisUtil.expire(RedisConstans.CARNIVAL_BOX_AWARDED_NUM, RedisConstans.ONE_MONTH);
		//生成随机数 0-4
		ActivityPrize prize = prizeList.get((int) (size - incr));
		//保存奖品到背包
		executor.execute(()->{
			giftService.savePrize(ACTIVITY_ID_1, carnivalBoxDto.getUserId(), prize.getGiftId(), 2, prize.getNum());
		});
		result.put("status", "1");
		GiftConfigVo giftConfigVo = new GiftConfigVo();
		giftConfigVo.setName(prize.getName()).setIcon(prize.getIcon()).setNum(prize.getNum());
		result.put("prize", giftConfigVo);
		//等于5时删除宝箱
		if(incr >= size && mapList.size() > 1){
			redisUtil.hdel(key, boxId.toString());
		}
		return result;
	}

	//获取宝箱奖池
	private List<ActivityPrize> getBoxBool(){
		List<ActivityPrize> prizeList = redisUtil.get(RedisConstans.CARNIVAL_BOX_PRIZE);
		if(CollectionUtils.isEmpty(prizeList)){
			LambdaQueryWrapper<ActivityPrize> wrapper = new LambdaQueryWrapper<>();
			wrapper.eq(ActivityPrize::getActivityId, ACTIVITY_ID_1).eq(ActivityPrize::getType, 2).eq(ActivityPrize::getStatus, 1);
			prizeList = activityPrizeMapper.selectList(wrapper);
			redisUtil.set(RedisConstans.CARNIVAL_BOX_PRIZE, prizeList, RedisConstans.ONE_WEEK);
		}
		return prizeList;
	}


	@Override
	public CarnivalBeastVo getInitBeastInfo(String userId) {
		/*//是否在活动时间内
		if(!DateUtil.isBetweenTime(BEAST_TIME_MAP.get("startTime"), BEAST_TIME_MAP.get("endTime"))){
			return;
		}*/
		LocalDateTime now = LocalDateTime.now();
		//返回
		CarnivalBeastVo result = getCommonBeastData(userId, now);
		return result;
	}

	@Override
	public void initBeastInfo() {
		updateBossInfo();
		log.error("methodName:initBeastInfo,初始化当前场次年兽作战数据...");
		ID = getIdByDuration(LocalDateTime.now());
		CarnivalBeastVo beastVo = BEAST_MAP.get(ID);
		HP = new AtomicInteger(beastVo.getTotalHp());
		HARM_MAP.clear();
		HARM_COUNT_MAP.clear();
		maxHarmInfo = null;
		lastHarmInfo = null;
		maxHarmUserId = null;
	}

	@Override
	public void updateBossInfo() {
		LambdaQueryWrapper<ActivityData> wrapper1 = new LambdaQueryWrapper<>();
		wrapper1.eq(ActivityData::getActivityId, ACTIVITY_ID_2).orderByAsc(ActivityData::getCreateTime);
		List<ActivityData> activityDataList = activityDataMapper.selectList(wrapper1);
		for (ActivityData activityData : activityDataList) {
			CarnivalBeastVo carnivalBeastVo = new CarnivalBeastVo();
			carnivalBeastVo.setId(activityData.getId());
			carnivalBeastVo.setTotalHp(Integer.valueOf(activityData.getContent()));
			carnivalBeastVo.setDuration(Integer.valueOf(activityData.getProperty0()));
			carnivalBeastVo.setStartTimeStr(DateUtil.localDateTimeToString(activityData.getCreateTime()));
			carnivalBeastVo.setStartTime(DateUtil.stringToStamp(carnivalBeastVo.getStartTimeStr())/1000);
			BEAST_MAP.put(activityData.getId(), carnivalBeastVo);
		}
	}

	@Override
	public CarnivalBeastVo beastInfo(String userId) {
		LocalDateTime now = LocalDateTime.now();
		//返回
		CarnivalBeastVo result = getCommonBeastData(userId, now);
		return result;
	}

	//根据当前时间获取id
	private Integer getIdByDuration(LocalDateTime now){
		Long nowTime = now.toEpochSecond(ZoneOffset.of("+8"));
		CarnivalBeastVo firstBeastVo = null;
		for (Map.Entry<Integer, CarnivalBeastVo> entry : BEAST_MAP.entrySet()) {
			CarnivalBeastVo beastVo = entry.getValue();
			Long time = beastVo.getStartTime() + beastVo.getDuration();
			if(nowTime <= time){
				return beastVo.getId();
			}
			firstBeastVo = entry.getValue();
		}
		return firstBeastVo == null ? null : firstBeastVo.getId();
	}

	private CarnivalBeastVo getCommonBeastData(String userId, LocalDateTime now){
		CarnivalBeastVo beastVo = BEAST_MAP.get(ID);
		Long nowTime = now.toEpochSecond(ZoneOffset.of("+8"));
		CarnivalBeastVo result = new CarnivalBeastVo();
		if(beastVo == null){
			result.setSurplusHp(100000);
			result.setSurplusSecond(3600L);
			result.setTotalHarm(0);
			result.setTotalHp(100000);
		} else{
			AtomicInteger harm = HARM_MAP.get(userId);
			if(nowTime < beastVo.getStartTime()){
				result.setSurplusSecond(3600L);
			} else {
				if(HP.intValue() <= 0){
					result.setSurplusSecond(0L);
				} else {
					long surplusSecond = beastVo.getStartTime() + beastVo.getDuration() - nowTime;
					result.setSurplusSecond(surplusSecond < 0 ? 0 : surplusSecond);
				}
			}
			result.setSurplusHp(HP.intValue());
			result.setTotalHarm(harm == null ? 0 : harm.intValue());
			result.setTotalHp(beastVo.getTotalHp());
			result.setStartTime(beastVo.getStartTime());
		}
		result.setTimeStamp(nowTime);
		result.setMaxHarmInfo(maxHarmInfo);
		result.setLastHarmInfo(lastHarmInfo);
		return result;
	}

	private boolean checkStart(){
		CarnivalBeastVo beastVo = BEAST_MAP.get(ID);
		Long nowTime = LocalDateTime.now().toEpochSecond(ZoneOffset.of("+8"));
		Long startTime = beastVo.getStartTime();
		Long endTime = startTime + beastVo.getDuration();
		if(nowTime >= startTime && nowTime <= endTime && HP.intValue() > 0){
			return true;
		}
		return false;
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
//	@CurrentLimiter(QPS = 50)
	public Result attackBeast(String userId, Integer harm) {
		//判断是否在游戏时间
		if(!checkStart()){
			return Result.error("当前不在作战时间内");
		}
		LocalDateTime now = LocalDateTime.now();
		//检查年兽血量
		if(HP.intValue() <= 0){
			return Result.error(-3, getNextStartTime());
		}
		UserWallet wallet = null;
		if(harm == 1000 || harm == 10000 || harm == 100000){
			wallet = userWalletMapper.selectByUserId(userId);
			Integer diamond = harm / 10;
			//检查钻石是否足够
			if (wallet == null || diamond > wallet.getDiamondsAmount()) {
				return Result.error(-2, "钱包数据不存在");
			}
		}
		//锁
		try {
			lock.lock();
			//检查年兽血量
			if(HP.intValue() <= 0){
				return Result.error(-3, getNextStartTime());
			}
			//先减少年兽血条
			HP.addAndGet(-harm);
		} finally {
			lock.unlock();
		}
		AtomicInteger freeCount = HARM_COUNT_MAP.get(userId);
		//钻石攻击
		if(harm == 1000 || harm == 10000 || harm == 100000){
			Integer diamond = harm / 10;
			//扣减钻石
			int row = userWalletMapper.reduceDiamondsAmount(wallet.getId(), diamond);
			if(row < 1){
				//扣除钻石失败则把boss血量加回去
				HP.addAndGet(harm);
				return Result.error("攻击年兽失败，请稍候重试");
			}
		} else if(harm == 1){
			//普通攻击
			//统计免费用户伤害次数
			if (freeCount == null) {
				HARM_COUNT_MAP.put(userId, new AtomicInteger(1));
			} else {
				freeCount.incrementAndGet();
				HARM_COUNT_MAP.put(userId, freeCount);
			}
		} else{
			return Result.error("伤害异常！");
		}

		//如果boss的血量小于0则调整为0
		if(HP.intValue() < 0){
			HP = new AtomicInteger(0);
		}
		//增加伤害
		AtomicInteger userHarm = HARM_MAP.get(userId);
		if(userHarm == null){
			userHarm = new AtomicInteger(0);
		}
		userHarm.addAndGet(harm);
		HARM_MAP.put(userId, userHarm);
		List<NewLotteryVo> lotteryList = new ArrayList<>();
		//抽奖是否中礼物
		if(harm == 1 && freeCount != null && (freeCount.intValue() == 10 || freeCount.intValue() == 50 || freeCount.intValue() % 100 ==0)){
			//免费中奖
			NewLotteryVo lottery = lotterySimple( ACTIVITY_ID_2, 0, null);
			lotteryList.add(lottery);
		} else if(harm == 1000){
			//消费钻石中奖
			lotteryList = lotteryBatch(userId,  1);
		} else if(harm == 10000){
			//消费钻石中奖
			lotteryList = lotteryBatch(userId,  10);
		} else if(harm == 100000){
			//消费钻石中奖
			lotteryList = lotteryBatch(userId,  100);
		}
		//保存奖品到背包
		List<NewLotteryVo> finalLotteryList = lotteryList;
		executor.execute(()->{
			User user = userService.getUser(userId);
			if (user == null) {
				return;
			}
			//伤害榜
			updateHarmRankings(user, harm);

			//备份当前回合实时数据到缓存
			Map<String, Object> map = new HashMap<>();
			map.put("ID", ID);
			map.put("HP", HP);
			map.put("HARM_MAP", HARM_MAP);
			map.put("HARM_COUNT_MAP", HARM_COUNT_MAP);
			map.put("maxHarmInfo", maxHarmInfo);
			map.put("lastHarmInfo", lastHarmInfo);
			map.put("maxHarmUserId", maxHarmUserId);
			redisUtil.set(RedisConstans.CARNIVAL_BEAST_DATA + ID, map, RedisConstans.ONE_MONTH);
			//后台缓存
			if(harm == 1){
				redisUtil.incr(RedisConstans.CARNIVAL_BEAST_FREE_HARM + ID);
			} else {
				redisUtil.incr(RedisConstans.CARNIVAL_BEAST_PAY_HARM + ID, harm, RedisConstans.ONE_MONTH);
			}

			//奖品处理
			if(CollectionUtils.isEmpty(finalLotteryList)){
				return;
			}
			for (NewLotteryVo lotteryVo : finalLotteryList) {
				//保存礼物到背包
				if(harm == 1){
					giftService.savePrize(ACTIVITY_ID_2, userId, lotteryVo.getGiftId(), 0, lotteryVo.getNum());
				} else{
					giftService.savePrize(ACTIVITY_ID_2, userId, lotteryVo.getGiftId(), 1, lotteryVo.getNum());
				}
				//记录大于880钻石的礼物到实时榜
				if(lotteryVo.getValue() >= 880){
					String means;
					if(harm == 1000){
						means = "钻石攻击×1";
					} else if(harm == 10000){
						means = "钻石攻击×10";
					}else{
						means = "钻石攻击×100";
					}
					CarnivalBeastVo realTime = new CarnivalBeastVo();
					realTime.setNickname(user.getNickname())
							.setRound(df.format(now))
							.setMeans(means)
							.setGiftName(lotteryVo.getName());
					redisUtil.lLeftSet(RedisConstans.CARNIVAL_BEAST_PRIZE_HEIGHT + ID, realTime, RedisConstans.ONE_MONTH);
				}
			}
		});
		//返回
		CarnivalBeastVo result = beastInfo(userId);
		//最后一击和最高伤害
		if(HP.intValue() <= 0){
			//结束游戏fightBeast
			//ING = false;
			String heightHarmUserId = getHeightHarmUserId();
			maxHarmUserId = heightHarmUserId;
			User maxUser = userService.getUser(heightHarmUserId);
			//发放特殊奖励
			if(HP.intValue() <= 0 && redisUtil.get(RedisConstans.CARNIVAL_BEAST_MAX_HARM + ID) == null){
				List<NewLotteryVo> maxPrizeList = getPrizeList(ACTIVITY_ID_2, 2);
				//保存礼物到用户背包
				giftService.savePrize(ACTIVITY_ID_2, heightHarmUserId,maxPrizeList.get(0).getGiftId(), 2, maxPrizeList.get(0).getNum());
				maxHarmInfo = (maxUser == null ? "" :maxUser.getNickname())+ "获得【" + maxPrizeList.get(0).getName() + "】";
				redisUtil.set(RedisConstans.CARNIVAL_BEAST_MAX_HARM + ID, 1, RedisConstans.ONE_MONTH);
				if(userId.equals(heightHarmUserId)){
					result.setMaxHarmPrize(maxPrizeList.get(0));
				}
			}
			if(HP.intValue() <= 0 && redisUtil.get(RedisConstans.CARNIVAL_BEAST_LAST_HARM + ID) == null){
				User user = userService.getUser(userId);
				List<NewLotteryVo> lastHarmList = getPrizeList(ACTIVITY_ID_2, 3);
				lastHarmInfo = user.getNickname() + "获得【" + lastHarmList.get(0).getName() + "】";
				//保存礼物到用户背包
				giftService.savePrize(ACTIVITY_ID_2, userId,lastHarmList.get(0).getGiftId(), 3, lastHarmList.get(0).getNum());
				redisUtil.set(RedisConstans.CARNIVAL_BEAST_LAST_HARM + ID, maxHarmInfo + "_" + lastHarmInfo, RedisConstans.ONE_MONTH);
				result.setLastHarmPrize(lastHarmList.get(0));
			}
			//下一场开始时间
			result.setMessage(getNextStartTime());
			result.setMaxHarmInfo(maxHarmInfo);
			result.setLastHarmInfo(lastHarmInfo);
		}
		//相同奖品合并
		List<NewLotteryVo> prizeList = mergePrize(lotteryList);
		result.setGiftList(prizeList);
		return Result.success(result);
	}

	//合并奖品
	private List<NewLotteryVo> mergePrize(List<NewLotteryVo> lotteryList){
		List<NewLotteryVo> prizeList = new ArrayList<>();
		if(CollectionUtils.isEmpty(lotteryList)){
			return prizeList;
		}
		Map<Integer, List<NewLotteryVo>> groupMap = lotteryList.stream().collect(Collectors.groupingBy(NewLotteryVo::getGiftId));
		Set<Map.Entry<Integer, List<NewLotteryVo>>> entries = groupMap.entrySet();
		for (Map.Entry<Integer, List<NewLotteryVo>> entry : entries) {
			NewLotteryVo record = new NewLotteryVo();
			List<NewLotteryVo> list = entry.getValue();
			NewLotteryVo lotteryVo = list.get(0);
			int num = 0;
			for (NewLotteryVo lotteryVo1 : list) {
				num += lotteryVo1.getNum();
			}
			BeanUtils.copyProperties(lotteryVo, record);
			record.setNum(num);
			prizeList.add(record);
		}
		return prizeList;
	}

	//获取最高伤害用户id
	private String getHeightHarmUserId(){
		int max = 0;
		String key = "";
		Set<Map.Entry<String, AtomicInteger>> entries = HARM_MAP.entrySet();
		for (Map.Entry<String, AtomicInteger> entry : entries) {
			if(entry.getValue().intValue() > max){
				max = entry.getValue().intValue();
				key = entry.getKey();
			}
		}
		return key;
	}


	//下一场开始时间
	@Override
	public String getNextStartTime(){
		if(ID == null){
			return "";
		}

		Set<Map.Entry<Integer, CarnivalBeastVo>> entries = BEAST_MAP.entrySet();
		boolean flag = false;
		for (Map.Entry<Integer, CarnivalBeastVo> entry : entries) {
			if(flag){
				return DateUtil.stringToLocalDateTime(entry.getValue().getStartTimeStr()).getHour() + "";

			}
			if(entry.getKey().equals(ID)){
				flag = true;
			}

		}
		return "";
	}


	//更新伤害榜
	private void updateHarmRankings( User user, Integer harm){


		CarnivalBeastVo harmRanking = (CarnivalBeastVo) redisUtil.hget(RedisConstans.CARNIVAL_BEAST_HEIGHT_HARM + ID, user.getUserId());
		if(harmRanking == null){
			harmRanking = new CarnivalBeastVo();
			harmRanking.setTotalHarm(harm)
					.setAccountId(user.getAccountId())
					.setNickname(user.getNickname())
					.setIcon(StringUtils.isNotBlank(user.getAvatarUrl()) ? user.getAvatarUrl() : Constants.DEFAULT_AVATAR);
		} else {
			harmRanking.setTotalHarm(harmRanking.getTotalHarm() + harm);
		}


		redisUtil.hset(RedisConstans.CARNIVAL_BEAST_HEIGHT_HARM + ID, user.getUserId(), harmRanking, RedisConstans.ONE_MONTH);
		redisUtil.zIncrScore(RedisConstans.CARNIVAL_BEAST_HEIGHT_HARM_VALUE + ID, user.getUserId(), harm.longValue(), RedisConstans.ONE_MONTH);
	}

	@Override
	public List<CarnivalBeastVo> harmRankings(CarnivalBeastDto carnivalBeastDto) {
		Set<Object> userIdSet = redisUtil.zRevRange(RedisConstans.CARNIVAL_BEAST_HEIGHT_HARM_VALUE + ID, 0, 9);
		List<CarnivalBeastVo> list = redisUtil.hMultiGet(RedisConstans.CARNIVAL_BEAST_HEIGHT_HARM + ID, userIdSet);
		if(CollectionUtils.isEmpty(list)){
			return new ArrayList<>();
		}
		return list;
	}

	@Override
	public List<CarnivalBeastVo> realTimeRankings() {
		List<CarnivalBeastVo> list = redisUtil.lGet(RedisConstans.CARNIVAL_BEAST_PRIZE_HEIGHT + ID, 0, 99);
		return list;
	}

	@Override
	public Map<String, GiftConfigVo> specialPrize(CarnivalCardDto carnivalCardDto) {
		Map<String, GiftConfigVo> result = new HashMap<>();
		LambdaQueryWrapper<ActivityPrize> wrapper = new LambdaQueryWrapper<>();
		wrapper.eq(ActivityPrize::getActivityId, ACTIVITY_ID_2).in(ActivityPrize::getType, 2,3).eq(ActivityPrize::getStatus, 1);
		List<ActivityPrize> prizeList = activityPrizeMapper.selectList(wrapper);
			for (ActivityPrize activityPrize : prizeList) {
				GiftConfigVo vo = new GiftConfigVo();
				vo.setName(activityPrize.getName()).setIcon(activityPrize.getIcon());
				if(activityPrize.getType() == 2){
					result.put("max",vo);
				} else 	if(activityPrize.getType() == 3){
					result.put("last",vo);
				}
			}
		return result;
	}

	@Override
	public PageVo<CarnivalCardVo> cardRankings(CarnivalCardDto carnivalCardDto) {
		if(carnivalCardDto.getPageNo() == null){
			carnivalCardDto.setPageNo(1);
		}
		if(carnivalCardDto.getPageSize() == null){
			carnivalCardDto.setPageSize(10);
		}
		PageVo<CarnivalCardVo> pageVo  = (PageVo) redisUtil.hget(RedisConstans.CARNIVAL_PUZZLE_RANKINGS, carnivalCardDto.getPageNo() + "_" + carnivalCardDto.getPageSize());
		if(pageVo != null && carnivalCardDto.getPageNo() == 1){
			return pageVo;
		}
		Page<CarnivalCardVo> page = new Page<>(carnivalCardDto.getPageNo(), carnivalCardDto.getPageSize());
		Page<CarnivalCardVo> pageData = activityCardMapper.selectGroupChannelCount(page, ACTIVITY_ID_1);
		for (CarnivalCardVo cardVo : pageData.getRecords()) {
			ChatChannel chatChannel = chatChannelService.getChatChannel(cardVo.getChannelId());
			if (chatChannel == null) {
				continue;
			}
			cardVo.setChannelName(chatChannel.getChannelName())
					.setAccountId(chatChannel.getOwnerAccountid())
					.setChannelIcon(StringUtils.isNotBlank(chatChannel.getAvatarUrl()) ? chatChannel.getAvatarUrl() : Constants.DEFAULT_AVATAR);
		}
		pageVo = new PageVo<>(pageData.getRecords(), pageData.getTotal());
		//pageVo = BaseUtil.page(pageData.getRecords(), carnivalCardDto.getPageNo(), carnivalCardDto.getPageSize());
		redisUtil.hset(RedisConstans.CARNIVAL_PUZZLE_RANKINGS, carnivalCardDto.getPageNo() + "_" + carnivalCardDto.getPageSize() ,pageVo,RedisConstans.ONE_DAY);
		return pageVo;
	}

	@Override
	public Map<String, Object> pushBeastInfo(){
		//1.有推送，api判断此值，有才请求推送接口
		redisUtil.set(RedisConstans.CARNIVAL_PUSH, 1, RedisConstans.TWO_MINUTES);
		//2.查询场次
		LocalDateTime now = LocalDateTime.now();
		int day = now.getDayOfMonth();
		int hour = now.getHour();
		String key = RedisConstans.CARNIVAL_PUSH_BEAST + day + "_" + hour;
		Map<String, Object> map = redisUtil.get(key);
		if(map != null){
			return map;
		}
		map = getPushBeastInfo(now);
		String msg = JSON.toJSONString(map);
		if(getSwitch()){
			log.error("推送走的是长连接....");
			mqProducer.sendPushInfo(msg);
		}
		redisUtil.set(key, map,RedisConstans.ONE_MONTH);
		return map;
	}

	@Override
	public Map<String, Object> getPush(CarnivalBoxDto carnivalBoxDto) {
		LocalDateTime now = LocalDateTime.now();
		Long currentTime = now.toEpochSecond(ZoneOffset.of("+8"));
		//年兽作战消息推送,开始时间后1分钟内发送
		for (Map.Entry<Integer, CarnivalBeastVo> entry : BEAST_MAP.entrySet()) {
			CarnivalBeastVo beastVo = entry.getValue();
			if(beastVo.getStartTime() <= currentTime && currentTime <= beastVo.getStartTime() + 60){
				String key = RedisConstans.CARNIVAL_PUSH_BEAST_ID  + beastVo.getStartTime() + "_" + carnivalBoxDto.getUserId();
				if(redisUtil.get(key) == null){
					Map<String, Object> beastMap = getPushBeastInfo(now);
					redisUtil.set(key, 1, RedisConstans.ONE_MONTH);
					return beastMap;
				}
				break;
			}
		}
		//宝箱消息推送
		List<Map<String, Object>> messageList = redisUtil.hGetValuesList(RedisConstans.CARNIVAL_PUSH_BOX);
		if(CollectionUtils.isEmpty(messageList)){
			return null;
		}
		for (Map<String, Object> map : messageList) {
			Long id = (Long) map.get("id");
			Long startTime = (Long) map.get("startTime");
			if(currentTime > startTime){
				redisUtil.hdel(RedisConstans.CARNIVAL_PUSH_BOX, id.toString());
				continue;
			}
			String key = RedisConstans.CARNIVAL_PUSH_BOX_ID  + id + "_" + carnivalBoxDto.getUserId();
			if(redisUtil.get(key) == null){
				redisUtil.set(key, 1, RedisConstans.ONE_MONTH);
				return map;
			}
		}
		return null;
	}

	private Map<String, Object> getPushBeastInfo(LocalDateTime now){
		int hour = now.getHour();
		/*if(hour == 0 || hour == 12  || hour == 18  || hour == 20  || hour == 22){
			return null;
		}*/
		Map<String, Object> beastMap = new HashMap<>();
		beastMap.put("content", String.format("<span style=\"font-size: 13px;font-weight: bold;color: white\"><font color=\"#FFEF39\">%s点</font>的年兽大作战开始啦！丰厚奖励等你来拿！》》前往打年兽</span>", hour));
		beastMap.put("linkValue", h5Domain + "/activityxb/#/beastBattle?token=${login_token}");
		beastMap.put("linkType", 2);
		beastMap.put("title", "年兽作战");
		return beastMap;
	}


	/**
	 *	抽奖
	 * @param userId
	 * @param times
	 * @return
	 */
	@Override
	public List<NewLotteryVo> lotteryBatch(String userId, Integer times) {
		// 获取所有抽奖奖品
		List<NewLotteryVo> prizeList = getPrizeList(ACTIVITY_ID_2, 1);
		// 计算所有概率的总和
		Integer sumProbability = prizeList.stream().mapToInt(t -> Integer.valueOf(t.getProbability())).sum();
		if(sumProbability == 0){
			return new ArrayList<>();
		}
		// 用户抽奖次数
		Integer roundCount = (Integer) redisUtil.hget(RedisConstans.ACTIVITY_LOTTERY_COUNT, userId);
		// 用户已抽奖的奖品总值
		Integer giftValue = (Integer) redisUtil.hget(RedisConstans.ACTIVITY_LOTTERY_PRIZE_VALUE, userId);
		// 我的中奖列表
		List<NewLotteryVo> myPrizeList = new ArrayList<>();
		for (int i = 0; i < times; i++) {
			if(giftValue == null || (roundCount != null && roundCount >= USER_TOTAL_ROUND)){
				giftValue = 0;
			}
			if (roundCount == null || roundCount >= USER_TOTAL_ROUND) {
				roundCount = 0;
			}
			//增加抽奖次数
			roundCount++;
			NewLotteryVo gift = CommonUtil.lotteryHigh(prizeList);
			log.warn("用户Id:{},抽数:{},当前抽奖次数:{},中【指定概率】中奖结果为:{},中奖概率:{}", userId, times, roundCount, gift.getName(), gift.getProbability());
			myPrizeList.add(gift);
			// 叠加当前用户抽奖奖品价值
			giftValue += gift.getValue();
			if (roundCount.equals(USER_TOTAL_ROUND) && giftValue != 0) {
				// 检查当前回合为最大回合时，生成对应补偿礼物
				log.warn("====个人抽奖回合结束,100抽奖品价值:{}====", giftValue);
				NewLotteryVo mustGift = checkPriceSpreadAndGet(prizeList, giftValue);
				if(mustGift != null){
					myPrizeList.add(mustGift);
					log.warn("====个人抽奖回合结束,需要补奖为:{}====", JSONObject.toJSONString(mustGift));
				}
			}
		}
		// 将当前查询数据保存到redis
		redisUtil.hset(RedisConstans.ACTIVITY_LOTTERY_COUNT, userId, roundCount, RedisConstans.ONE_MONTH);
		redisUtil.hset(RedisConstans.ACTIVITY_LOTTERY_PRIZE_VALUE, userId, giftValue, RedisConstans.ONE_MONTH);
		//返回结果
		return myPrizeList;
	}

	/**
	 * 简单抽奖
	 * @return
	 */
	public NewLotteryVo lotterySimple(Integer activityId, Integer type, List<NewLotteryVo> prizeList) {
		if(CollectionUtils.isEmpty(prizeList)){
			prizeList = getPrizeList(activityId, type);
		}
		Integer sum = prizeList.stream().mapToInt(t -> t.getProbability()).sum();
		int rate = 0;
		int random = RandomUtil.randomInt(0, sum);
		for (NewLotteryVo item : prizeList) {
			// 中奖概率
			rate += item.getProbability();
			if (random < rate) {
				item.setProbability(null);
				return item;
			}
		}
		return null;
	}

	//获取奖池
	@Override
	public List<NewLotteryVo> getPrizeList(Integer activityId, Integer type){
		return activityPrizeMapper.selectPrizes(activityId, type);
	}

	/**
	 * 判断获得礼物价值和总礼物价值差生成来相应个人的奖池数据，并返回个人奖池的抽奖奖品概率列表
	 */
	private NewLotteryVo checkPriceSpreadAndGet(List<NewLotteryVo> giftConfigList, Integer giftValue) {
		NewLotteryVo result = null;
		for (NewLotteryVo newLotteryVo : giftConfigList) {
			if (giftValue < 5000) {// 出5千钻礼物
				if (newLotteryVo.getValue().equals(6660)) {
					result = newLotteryVo;
					break;
				}
			} else if (giftValue >= 5000 && giftValue < 8000) {
				if (newLotteryVo.getValue().equals(2100)) {
					result = newLotteryVo;
					break;
				}
			} else if (giftValue >= 8000 && giftValue < 9000) {
				if (newLotteryVo.getValue().equals(880)) {
					result = newLotteryVo;
					break;
				}
			}
		}
		return result;
	}

	//判断开关，开为走长连接，关走实时信息接口
	private boolean getSwitch(){
		SysDict sysDict = sysDictMapper.selectOne(new LambdaQueryWrapper<SysDict>().eq(SysDict::getCode, "NOTIFY_SWITCH").eq(SysDict::getName, "ACTIVITY"));
		if(sysDict == null || StringUtils.isBlank(sysDict.getProperty1()) || StringUtils.isBlank(sysDict.getProperty2())){
			return false;
		}
		if("1".equals(sysDict.getProperty1()) || "1".equals(sysDict.getProperty2())){
			return true;
		}
		return false;
	}
}
